package blue.http.internal.core.parser;

import blue.base.core.path.FilePath;
import blue.base.core.reflect.BeanMethod;
import blue.base.core.reflect.JavaBean;
import blue.http.core.annotation.Charset;
import blue.http.core.annotation.ContentType;
import blue.http.core.annotation.Http;
import blue.http.core.annotation.HttpMethod;
import blue.http.core.exception.HttpServerException;
import blue.http.internal.core.parser.parameter.ParamParserFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Jin Zheng
 * @since 1.0 2021-08-05
 */
public class HttpParser extends AbstractParser {
	private static Logger logger = LoggerFactory.getLogger(HttpParser.class);

	private static final Set<HttpMethod> METHOD_SET = HttpMethod.all();
	private final ParamParserFactory paramParserFactory;
	private final HttpConfigCache configCache;

	public HttpParser(ParamParserFactory paramParserFactory, HttpConfigCache httpConfigCache) {
		this.paramParserFactory = paramParserFactory;
		this.configCache = httpConfigCache;
	}

	@Override
	protected void parseMethod(JavaBean bean, BeanMethod method, FilePath rootPath) {
		Set<HttpMethod> httpMethodSet = new HashSet<>();
		var annotationHttp = bean.getAnnotation(Http.class);
		this.addHttpMethod(httpMethodSet, annotationHttp.method());

		Charset charset = annotationHttp.charset();
		ContentType contentType = annotationHttp.contentType();
		String name = annotationHttp.name();
		FilePath uriPath = rootPath.copy();
		Http annotationMethod = method.getAnnotation(Http.class);
		if (annotationMethod != null) {
			uriPath.concat(annotationMethod.url());
			charset = annotationMethod.charset();
			contentType = annotationMethod.contentType();
			name = annotationMethod.name();
			httpMethodSet.clear();
			this.addHttpMethod(httpMethodSet, annotationMethod.method());
		}
		if (httpMethodSet.isEmpty()) {
			if (annotationHttp.method().length == 0) {
				httpMethodSet.addAll(METHOD_SET);
			} else {
				this.addHttpMethod(httpMethodSet, annotationHttp.method());
			}
		}
		List<RequestParamConfig> paramConfigList = paramParserFactory.parse(method);
		String url = uriPath.getCurrentPath();
		for (HttpMethod httpMethod : httpMethodSet) {
			DefaultHttpUrlConfig config = new DefaultHttpUrlConfig();
			config.setName(name.isEmpty() ? url : name);
			config.setUrl(url);
			config.setHttpMethod(httpMethod);
			config.setCharset(charset);
			config.setContentType(contentType);
			config.setJavaBean(bean);
			config.setMethod(method);
			config.setParamList(paramConfigList);
			HttpUrlKey key = config.buildKey();

			if (configCache.contains(key)) {
				throw new HttpServerException("url 已经存在: " + config);
			}

			configCache.put(key, config);
			logger.info("Found Http: {} [{}] [{}] [{}]，{}.{}()", config.getUrl(), httpMethod.name(),
					charset.name(), contentType, bean.getTargetClass().getSimpleName(), method.getName());
		}
	}

	private void addHttpMethod(Set<HttpMethod> httpMethodSet, HttpMethod[] httpMethods) {
		if (httpMethods == null || httpMethods.length == 0) {
			return;
		}

		for (HttpMethod httpMethod : httpMethods) {
			if (!METHOD_SET.contains(httpMethod)) {
				throw new HttpServerException("Unsupported http method: " + httpMethod.name());
			}

			httpMethodSet.add(httpMethod);
		}
	}

	@Override
	protected FilePath getRootPath(JavaBean bean) {
		Http annotation = bean.getAnnotation(Http.class);
		return FilePath.create(annotation.url());
	}

}
